home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / fileutil / fileutils-3.16.tar.gz / fileutils-3.16.tar / fileutils-3.16 / lib / makepath.c < prev    next >
C/C++ Source or Header  |  1996-11-04  |  7KB  |  302 lines

  1. /* makepath.c -- Ensure that a directory path exists.
  2.    Copyright (C) 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software Foundation,
  16.    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  17.  
  18. /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Jim Meyering.  */
  19.  
  20. #if HAVE_CONFIG_H
  21. # include <config.h>
  22. #endif
  23.  
  24. #if __GNUC__
  25. # define alloca __builtin_alloca
  26. #else
  27. # if HAVE_ALLOCA_H
  28. #  include <alloca.h>
  29. # else
  30. #  ifdef _AIX
  31.  #pragma alloca
  32. #  else
  33. char *alloca ();
  34. #  endif
  35. # endif
  36. #endif
  37.  
  38. #include <stdio.h>
  39. #include <sys/types.h>
  40. #include <sys/stat.h>
  41. #if HAVE_UNISTD_H
  42. # include <unistd.h>
  43. #endif
  44.  
  45. #if STAT_MACROS_BROKEN
  46. # undef S_ISDIR
  47. #endif
  48.  
  49. #if !defined(S_ISDIR) && defined(S_IFDIR)
  50. # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  51. #endif
  52.  
  53. #if STDC_HEADERS
  54. # include <stdlib.h>
  55. #endif
  56.  
  57. #if HAVE_ERRNO_H
  58. # include <errno.h>
  59. #endif
  60.  
  61. #ifndef errno
  62. extern int errno;
  63. #endif
  64.  
  65. #if HAVE_STRING_H
  66. # include <string.h>
  67. #else
  68. # include <strings.h>
  69. # ifndef strchr
  70. #  define strchr index
  71. # endif
  72. #endif
  73.  
  74. #ifdef __MSDOS__
  75. typedef int uid_t;
  76. typedef int gid_t;
  77. #endif
  78.  
  79. #include "makepath.h"
  80.  
  81. void error ();
  82.  
  83. /* Ensure that the directory ARGPATH exists.
  84.    Remove any trailing slashes from ARGPATH before calling this function.
  85.  
  86.    Create any leading directories that don't already exist, with
  87.    permissions PARENT_MODE.
  88.    If the last element of ARGPATH does not exist, create it as
  89.    a new directory with permissions MODE.
  90.    If OWNER and GROUP are non-negative, use them to set the UID and GID of
  91.    any created directories.
  92.    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
  93.    string for printing a message after successfully making a directory,
  94.    with the name of the directory that was just made as an argument.
  95.    If PRESERVE_EXISTING is non-zero and ARGPATH is an existing directory,
  96.    then do not attempt to set its permissions and ownership.
  97.  
  98.    Return 0 if ARGPATH exists as a directory with the proper
  99.    ownership and permissions when done, otherwise 1.  */
  100.  
  101. #if __STDC__
  102. int
  103. make_path (const char *argpath,
  104.        int mode,
  105.        int parent_mode,
  106.        uid_t owner,
  107.        gid_t group,
  108.        int preserve_existing,
  109.        const char *verbose_fmt_string)
  110. #else
  111. int
  112. make_path (argpath, mode, parent_mode, owner, group, preserve_existing,
  113.        verbose_fmt_string)
  114.      const char *argpath;
  115.      int mode;
  116.      int parent_mode;
  117.      uid_t owner;
  118.      gid_t group;
  119.      int preserve_existing;
  120.      const char *verbose_fmt_string;
  121. #endif
  122. {
  123.   char *dirpath;        /* A copy we can scribble NULs on.  */
  124.   struct stat stats;
  125.   int retval = 0;
  126.   int oldmask = umask (0);
  127.  
  128.   /* FIXME: move this alloca and strcpy into the if-block.
  129.      Set dirpath to argpath in the else-block.  */
  130.   dirpath = (char *) alloca (strlen (argpath) + 1);
  131.   strcpy (dirpath, argpath);
  132.  
  133.   if (stat (dirpath, &stats))
  134.     {
  135.       char *slash;
  136.       int tmp_mode;        /* Initial perms for leading dirs.  */
  137.       int re_protect;        /* Should leading dirs be unwritable? */
  138.       struct ptr_list
  139.       {
  140.     char *dirname_end;
  141.     struct ptr_list *next;
  142.       };
  143.       struct ptr_list *p, *leading_dirs = NULL;
  144.  
  145.       /* If leading directories shouldn't be writable or executable,
  146.      or should have set[ug]id or sticky bits set and we are setting
  147.      their owners, we need to fix their permissions after making them.  */
  148.       if (((parent_mode & 0300) != 0300)
  149.       || (owner != (uid_t) -1 && group != (gid_t) -1
  150.           && (parent_mode & 07000) != 0))
  151.     {
  152.       tmp_mode = 0700;
  153.       re_protect = 1;
  154.     }
  155.       else
  156.     {
  157.       tmp_mode = parent_mode;
  158.       re_protect = 0;
  159.     }
  160.  
  161.       slash = dirpath;
  162.       while (*slash == '/')
  163.     slash++;
  164.       while ((slash = strchr (slash, '/')))
  165.     {
  166.       *slash = '\0';
  167.       if (stat (dirpath, &stats))
  168.         {
  169.           if (mkdir (dirpath, tmp_mode))
  170.         {
  171.           error (0, errno, "cannot create directory `%s'", dirpath);
  172.           umask (oldmask);
  173.           return 1;
  174.         }
  175.           else
  176.         {
  177.           if (verbose_fmt_string != NULL)
  178.             error (0, 0, verbose_fmt_string, dirpath);
  179.  
  180.           if (owner != (uid_t) -1 && group != (gid_t) -1
  181.               && chown (dirpath, owner, group)
  182. #if defined(AFS) && defined (EPERM)
  183.               && errno != EPERM
  184. #endif
  185.               )
  186.             {
  187.               error (0, errno, "%s", dirpath);
  188.               retval = 1;
  189.             }
  190.           if (re_protect)
  191.             {
  192.               struct ptr_list *new = (struct ptr_list *)
  193.             alloca (sizeof (struct ptr_list));
  194.               new->dirname_end = slash;
  195.               new->next = leading_dirs;
  196.               leading_dirs = new;
  197.             }
  198.         }
  199.         }
  200.       else if (!S_ISDIR (stats.st_mode))
  201.         {
  202.           error (0, 0, "`%s' exists but is not a directory", dirpath);
  203.           umask (oldmask);
  204.           return 1;
  205.         }
  206.  
  207.       *slash++ = '/';
  208.  
  209.       /* Avoid unnecessary calls to `stat' when given
  210.          pathnames containing multiple adjacent slashes.  */
  211.       while (*slash == '/')
  212.         slash++;
  213.     }
  214.  
  215.       /* We're done making leading directories.
  216.      Create the final component of the path.  */
  217.  
  218.       /* The path could end in "/." or contain "/..", so test
  219.      if we really have to create the directory.  */
  220.  
  221.       if (stat (dirpath, &stats) && mkdir (dirpath, mode))
  222.     {
  223.       error (0, errno, "cannot create directory `%s'", dirpath);
  224.       umask (oldmask);
  225.       return 1;
  226.     }
  227.       if (verbose_fmt_string != NULL)
  228.     error (0, 0, verbose_fmt_string, dirpath);
  229.  
  230.       if (owner != (uid_t) -1 && group != (gid_t) -1)
  231.     {
  232.       if (chown (dirpath, owner, group)
  233. #ifdef AFS
  234.           && errno != EPERM
  235. #endif
  236.           )
  237.         {
  238.           error (0, errno, "%s", dirpath);
  239.           retval = 1;
  240.         }
  241.       /* chown may have turned off some permission bits we wanted.  */
  242.       if ((mode & 07000) != 0 && chmod (dirpath, mode))
  243.         {
  244.           error (0, errno, "%s", dirpath);
  245.           retval = 1;
  246.         }
  247.     }
  248.  
  249.       /* If the mode for leading directories didn't include owner "wx"
  250.      privileges, we have to reset their protections to the correct
  251.      value.  */
  252.       for (p = leading_dirs; p != NULL; p = p->next)
  253.     {
  254.       *(p->dirname_end) = '\0';
  255.       if (chmod (dirpath, parent_mode))
  256.         {
  257.           error (0, errno, "%s", dirpath);
  258.           retval = 1;
  259.         }
  260.     }
  261.     }
  262.   else
  263.     {
  264.       /* We get here if the entire path already exists.  */
  265.  
  266.       if (!S_ISDIR (stats.st_mode))
  267.     {
  268.       error (0, 0, "`%s' exists but is not a directory", dirpath);
  269.       umask (oldmask);
  270.       return 1;
  271.     }
  272.  
  273.       if (!preserve_existing)
  274.     {
  275.       /* chown must precede chmod because on some systems,
  276.          chown clears the set[ug]id bits for non-superusers,
  277.          resulting in incorrect permissions.
  278.          On System V, users can give away files with chown and then not
  279.          be able to chmod them.  So don't give files away.  */
  280.  
  281.       if (owner != (uid_t) -1 && group != (gid_t) -1
  282.           && chown (dirpath, owner, group)
  283. #ifdef AFS
  284.           && errno != EPERM
  285. #endif
  286.           )
  287.         {
  288.           error (0, errno, "%s", dirpath);
  289.           retval = 1;
  290.         }
  291.       if (chmod (dirpath, mode))
  292.         {
  293.           error (0, errno, "%s", dirpath);
  294.           retval = 1;
  295.         }
  296.     }
  297.     }
  298.  
  299.   umask (oldmask);
  300.   return retval;
  301. }
  302.